package com.ganlucode.sipmock.utils;

import com.ganlucode.sipmock.gb28181.constants.XmlNode;
import com.ganlucode.sipmock.gb28181.dto.XmlBean;
import com.thoughtworks.xstream.XStream;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.io.StringReader;
import java.util.*;

/**
 * @author GanLu
 * @date 2020-05-13 16:26
 */
@Slf4j
public class XmlUtil {


    public static <T extends XmlBean> T toXmlBean(Class<T> clazz, String xml) {
        try {
            T bean = clazz.newInstance();
            return bean.fromXmlString(xml);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T extends XmlBean> T toXmlBean(Class<T> clazz, InputStream inputStream) {
        try {
            T bean = clazz.newInstance();
            return bean.fromXmlString(inputStream);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T xmlToBean(Class<T> clazz, String xml) {
        return xmlToBean(getXStream(clazz), xml);
    }

    public static <T> T xmlToBean(XStream xstream, String xml) {
        return xmlToBean(xstream, xml, null);
    }

    public static <T> T xmlToBean(XStream xstream, String xml, T root) {
        try {
            return (T) xstream.fromXML(xml, root);
        } catch (Exception e) {
            throw new RuntimeException("XML转对象出错", e);
        }
    }

    public static <T> T xmlToBean(Class<T> clazz, InputStream inputStream) {
        return xmlToBean(getXStream(clazz), inputStream);
    }

    public static <T> T xmlToBean(XStream xstream, InputStream inputStream) {
        return xmlToBean(xstream, inputStream, null);
    }

    public static <T> T xmlToBean(XStream xstream, InputStream inputStream, T root) {
        try {
            return (T) xstream.fromXML(inputStream, root);
        } catch (Exception e) {
            throw new RuntimeException("XML转对象出错", e);
        }
    }

    public static <T> String beanToXml(T bean) {
        try {
            XStream xstream = getXStream(bean.getClass());
            return xstream.toXML(bean);
        } catch (Exception e) {
            throw new RuntimeException("对象转XML出错",e);
        }
    }
    public static <T> String beanToXml(T bean, XStream xstream) {
        try {
            return xstream.toXML(bean);
        } catch (Exception e) {
            throw new RuntimeException("对象转XML出错",e);
        }
    }

    public static <T> String beanToXmlWithHeader(T bean, XStream xstream, String version, String encoding) {
        try {
            StringBuilder sb = new StringBuilder();
            sb.append(String.format("<?xml version=\"%s\" encoding=\"%s\"?>\n", version, encoding));
            sb.append(xstream.toXML(bean));
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("对象转XML出错",e);
        }
    }

    public static <T> String beanToXmlWithHeader(T bean) {
        return beanToXmlWithHeader(bean, getXStream(bean.getClass()), XmlNode.XML_VERSION, XmlNode.XML_ENCODING);
    }

    public static <T> String beanToXmlWithHeader(T bean, XStream xstream) {
        return beanToXmlWithHeader(bean, xstream, XmlNode.XML_VERSION, XmlNode.XML_ENCODING);
    }

    public static <T> XStream getXStream(Class<T> clazz){
        XStream xstream = new XStream();
        XStream.setupDefaultSecurity(xstream);
        xstream.ignoreUnknownElements();
        xstream.processAnnotations(clazz);
        xstream.autodetectAnnotations(true);
        xstream.setClassLoader(clazz.getClassLoader());
        xstream.allowTypesByRegExp(new String[] { ".*" });
        return xstream;
    }

    /**
     * 解析XML为Document对象
     *
     * @param xml 被解析的XMl
     * @return Document
     */
    public static Element parseXml(String xml) {
        Document document = null;
        //
        StringReader sr = new StringReader(xml);
        SAXReader saxReader = new SAXReader();
        try {
            document = saxReader.read(sr);
        } catch (DocumentException e) {
            log.error("解析失败", e);
        }
        return null == document ? null : document.getRootElement();
    }

    /**
     * 获取element对象的text的值
     *
     * @param em  节点的对象
     * @param tag 节点的tag
     * @return 节点
     */
    public static String getText(Element em, String tag) {
        if (null == em) {
            return null;
        }
        Element e = em.element(tag);
        //
        return null == e ? null : e.getText();
    }

    /**
     * 递归解析xml节点，适用于 多节点数据
     *
     * @param node     node
     * @param nodeName nodeName
     * @return List<Map   <   String   ,       Object>>
     */
    public static List<Map<String, Object>> listNodes(Element node, String nodeName) {
        if (null == node) {
            return null;
        }
        // 初始化返回
        List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
        // 首先获取当前节点的所有属性节点
        List<Attribute> list = node.attributes();

        Map<String, Object> map = null;
        // 遍历属性节点
        for (Attribute attribute : list) {
            if (nodeName.equals(node.getName())) {
                if (null == map) {
                    map = new HashMap<String, Object>();
                    listMap.add(map);
                }
                // 取到的节点属性放到map中
                map.put(attribute.getName(), attribute.getValue());
            }

        }
        // 遍历当前节点下的所有节点 ，nodeName 要解析的节点名称
        // 使用递归
        Iterator<Element> iterator = node.elementIterator();
        while (iterator.hasNext()) {
            Element e = iterator.next();
            listMap.addAll(listNodes(e, nodeName));
        }
        return listMap;
    }

}
